package com.zhuangjie.coupon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhaungjie.base.conduit.TypeR;
import com.zhaungjie.base.utils.InstanceCloseUtils;
import com.zhaungjie.base.utils.PropertiesCopyUtils.PropertiesCopy;
import com.zhuangjie.coupon.dto.ShopCouponBatchDTO;
import com.zhuangjie.coupon.entity.Coupon;
import com.zhuangjie.coupon.entity.CouponBatch;
import com.zhuangjie.coupon.enums.*;
import com.zhuangjie.coupon.feign.ShopFeignService;
import com.zhuangjie.coupon.lock.CouponBatchLock;
import com.zhuangjie.coupon.mapper.CouponBatchMapper;
import com.zhuangjie.coupon.service.CouponBatchService;
import com.zhuangjie.coupon.service.CouponService;
import com.zhuangjie.security.provider.UserProvider;
import com.zhuangjie.shop.helper.ShopModuleTokenDataProvider;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhuangjie
 * @since 2024-02-28
 */
@Service
public class CouponBatchServiceImpl extends ServiceImpl<CouponBatchMapper, CouponBatch> implements CouponBatchService {
    @Autowired
    private Redisson redisson;

    @Autowired
    private CouponService couponService;

    @Autowired
    private ShopFeignService shopFeignService;

    @Override
    public void sendCouponToUser(CouponBatch couponBatch, Integer number, Long currentUserId) {
        // 申请
        RLock unlockAfterApplyCoupon = this.applyCoupon(couponBatch.getId(), number);
        try {
            Coupon coupon = new Coupon();
            coupon.setBatchId(couponBatch.getId());
            coupon.setUserId(currentUserId);
            coupon.setState(CouponState.UNUSED.getCode());
            coupon.setExpirationTime(calculateCouponExpirationTime(couponBatch));
            List<Coupon> coupons = InstanceCloseUtils.shallowCopy(coupon, number, true);
            // 批量保存（发送）优惠卷给用户
            boolean batchSaveSuccess = couponService.saveBatch(coupons);
            if (!batchSaveSuccess) throw new RuntimeException("发优惠卷失败！");
        } finally {
            unlockAfterApplyCoupon.unlock();
        }
    }

    private LocalDateTime calculateCouponExpirationTime(CouponBatch couponBatch) {
        if (Objects.equals(CouponUsageValidityPeriodType.RELATIVE_VALIDITY.getCode(), couponBatch.getUsageValidityPeriodType())) {
            if (couponBatch.getUsagePeriodDays() == null) {
                throw new RuntimeException("优惠卷相对有效期天数不能为空");
            }
            return LocalDateTime.now().plusDays(couponBatch.getUsagePeriodDays());
        } else if (Objects.equals(CouponUsageValidityPeriodType.FIXED_DATE.getCode(), couponBatch.getUsageValidityPeriodType())) {
            return couponBatch.getUsageEndTime();
        }
        throw new RuntimeException("不存在的有效期时间类型！");
    }

    /**
     * 申请优惠卷
     */
    public RLock applyCoupon(Long couponBatchId, Integer applyCount) {
        RLock lock = redisson.getLock(CouponBatchLock.getCouponBatchOperationLock(couponBatchId));
        lock.lock();
        try {
            CouponBatch couponBatch = this.getById(couponBatchId);
            // 1. 校验优惠卷是否可领取
            if (Objects.equals(couponBatch.getStatus(), CouponBatchStatus.UN_CLAIMABLE))
                throw new RuntimeException("优惠卷不可领取");
            // 2. 检验优惠卷是否满足申请数量
            Integer total = couponBatch.getTotal();
            // 申请后的IssuedCount
            Integer issuedCountAfterApplication = couponBatch.getIssuedCount() + applyCount;
            if (total != null && issuedCountAfterApplication > total) {
                throw new RuntimeException("优惠卷申请数量超过总优惠卷数量限制");
            }
            // 3. 检验是否已经领取超过每人可领取数量
            if (couponBatch.getPerPersonLimit() != null) {
                // 3.1 查询用户已经领取的数量
                LambdaQueryWrapper<Coupon> queryReceivedCouponCountQueryWrapper = Wrappers.lambdaQuery(Coupon.class)
                        .eq(Coupon::getUserId, UserProvider.currentUserId())
                        .eq(Coupon::getBatchId, couponBatch.getId());
                long userReceivedThisCouponCount = couponService.count(queryReceivedCouponCountQueryWrapper);
                // 3.2 判断
                if (userReceivedThisCouponCount >= couponBatch.getPerPersonLimit()) {
                    throw new RuntimeException("已经领取超过每个用户可领取的数量！");
                }
            }
            // 4. 锁定
            LambdaUpdateWrapper<CouponBatch> updateWrapper = Wrappers.lambdaUpdate(CouponBatch.class)
                    .eq(CouponBatch::getId, couponBatchId)
                    .set(CouponBatch::getIssuedCount, issuedCountAfterApplication);
            boolean isUpdateSuccess = this.update(updateWrapper);
            if (!isUpdateSuccess) throw new RuntimeException("优惠卷锁定失败");
        } catch (Exception e) {
            e.printStackTrace();
            lock.unlock();
        }
        return lock;
    }

    @Override
    public void putShopCouponBatch(ShopCouponBatchDTO shopCouponBatchDTO) throws Exception {
        // 1. 检查参数合法性
        checkShopCouponBatchDTO(shopCouponBatchDTO);
        // 1.2 维护输入数据的合法性
        maintainedInputShopCouponBatchDTOValidity(shopCouponBatchDTO);
        // 2. 构建优惠券批次对象
        CouponBatch couponBatch = PropertiesCopy.type(ShopCouponBatchDTO.class, CouponBatch.class)
                .produce(shopCouponBatchDTO);
        couponBatch.setShopId(ShopModuleTokenDataProvider.getShopId());
        // 3. 如果是初次创建，设置一此初始值
        if (shopCouponBatchDTO.getId() == null) {
            // 初次创建，如果状态为null，设置为可领取
            if (couponBatch.getStatus() == null) {
                couponBatch.setStatus(CouponBatchStatus.CLAIMABLE.getCode());
            }
            couponBatch.setIssuedCount(0);
            couponBatch.setUsedCount(0);
            couponBatch.setIssueType(IssuedType.SHOP_COUPON.getCode());
            // 生成使用规则描述信息
            couponBatch.setDescribe(generatorUsageDescription(shopCouponBatchDTO));
        }
        // 3. 保存到数据库
        if (shopCouponBatchDTO.getId() == null) {
            // 保存到数据库
            boolean isSaveSuccess = this.save(couponBatch);
            if (!isSaveSuccess) throw new RuntimeException("优惠券批次保存失败");
        } else {
            // 更新到数据库
            boolean isUpdateSuccess = this.updateById(couponBatch);
            if (!isUpdateSuccess) throw new RuntimeException("优惠券批次更新失败");
        }
    }

    /**
     * 维护创建店铺优惠券输入数据的合法性
     *
     * @param shopCouponBatchDTO
     */
    private void maintainedInputShopCouponBatchDTOValidity(ShopCouponBatchDTO shopCouponBatchDTO) {
        if (Objects.equals(shopCouponBatchDTO.getUsageValidityPeriodType(), CouponUsageValidityPeriodType.FIXED_DATE.getCode())) {
            shopCouponBatchDTO.setUsagePeriodDays(null);
        } else if (Objects.equals(shopCouponBatchDTO.getUsageValidityPeriodType(), CouponUsageValidityPeriodType.RELATIVE_VALIDITY.getCode())) {
            shopCouponBatchDTO.setUsageStartTime(null);
            shopCouponBatchDTO.setUsageEndTime(null);
        }
    }

    @Override
    public List<CouponBatch> queryShopCouponBatchList() {
        LambdaQueryWrapper<CouponBatch> queryShopCouponBatchListWrapper = Wrappers.lambdaQuery(CouponBatch.class)
                .eq(CouponBatch::getShopId, ShopModuleTokenDataProvider.getShopId());
        return this.list(queryShopCouponBatchListWrapper);
    }

    @Override
    public void removeShopCouponBatchById(Long couponBatchId) {
        LambdaQueryWrapper<CouponBatch> removeShopCouponBatchByIdWrapper = Wrappers.lambdaQuery(CouponBatch.class)
                .eq(CouponBatch::getId, couponBatchId)
                .eq(CouponBatch::getShopId, ShopModuleTokenDataProvider.getShopId());
        boolean isRemoveSuccess = this.remove(removeShopCouponBatchByIdWrapper);
        if (!isRemoveSuccess) throw new RuntimeException("优惠券批次删除失败");
    }

    @Override
    public ShopCouponBatchDTO getShopCouponBatchById(Long couponBatchId) {
        LambdaQueryWrapper<CouponBatch> queryShopCouponBatchByIdWrapper = Wrappers.lambdaQuery(CouponBatch.class)
                .eq(CouponBatch::getId, couponBatchId)
                .eq(CouponBatch::getShopId, ShopModuleTokenDataProvider.getShopId());
        CouponBatch couponBatch = this.getOne(queryShopCouponBatchByIdWrapper);
        if (couponBatch == null) throw new RuntimeException("优惠券批次不存在");
        return PropertiesCopy.type(CouponBatch.class, ShopCouponBatchDTO.class).produce(couponBatch);
    }

    private String generatorUsageDescription(ShopCouponBatchDTO shopCouponBatchDTO) throws Exception {
        StringBuilder usageDescription = new StringBuilder();
        usageDescription.append("- ");
        if (Objects.equals(shopCouponBatchDTO.getType(), CouponType.FULL_REDUCTION.getCode())) {
            usageDescription.append(shopCouponBatchDTO.getParValue()).append("元代金券，指定可用的商品满").append(shopCouponBatchDTO.getThresholdAmount()).append("元可用");
        } else if (Objects.equals(shopCouponBatchDTO.getType(), CouponType.DISCOUNT.getCode())) {
            usageDescription.append(shopCouponBatchDTO.getParValue()).append("折折扣券");
        }
        usageDescription.append(";\n");
        usageDescription.append("- ");
        if (Objects.equals(shopCouponBatchDTO.getApplicableType(), CouponApplicationType.NO_LIMIT.getCode())) {
            usageDescription.append("店铺内任何商品可使用");
        } else {
            // if(Objects.equals(shopCouponBatchDTO.getApplicableType(), CouponApplicationType.APPLICABLE_CATEGORY.getCode() ))
            usageDescription.append(Objects.equals(shopCouponBatchDTO.getApplicableLogic(), CouponApplicationLogicType.APPLICABLE_SELECTION.getCode()) ? "仅适用于" : "不适用于").append("指定的部分");
            if (Objects.equals(shopCouponBatchDTO.getApplicableType(), CouponApplicationType.APPLICABLE_CATEGORY.getCode())) {
                // 查询指定的类目id对应的名称
                Map<Long, String> categoryIdNameMap = TypeR.extract(shopFeignService.queryCategoryNames(shopCouponBatchDTO.getApplicableValues()));
                String categoryNames = String.join("、", categoryIdNameMap.values());
                usageDescription.append("类别下商品，类别：").append(categoryNames);
            } else {
                // 查询指定的菜品id对应的名称
                Map<Long, String> dishIdNameMap = TypeR.extract(shopFeignService.queryDishNames(shopCouponBatchDTO.getApplicableValues()));
                String categoryNames = String.join("、", dishIdNameMap.values());
                usageDescription.append("商品：").append(categoryNames);
            }
        }
        usageDescription.append(";\n");
        usageDescription.append("- ");
        // 是否可叠加描述
        if (shopCouponBatchDTO.getIsStackable()) {
            usageDescription.append("可叠加使用");
        } else {
            usageDescription.append("不可叠加使用");
        }
        usageDescription.append(";\n");
        return usageDescription.toString();
    }

//    private void checkShopCouponBatchDTO(ShopCouponBatchDTO shopCouponBatchDTO) {
//        Byte type = shopCouponBatchDTO.getType();
//        if (Objects.equals(CouponType.FULL_REDUCTION.getCode(), type)) {
//            // 如果是满减，优惠券值 > 0 且小于门槛值
//            if (shopCouponBatchDTO.getParValue() == null || shopCouponBatchDTO.getParValue().compareTo(BigDecimal.ZERO) <= 0) {
//                throw new RuntimeException("优惠卷值必须大于0！");
//            }
//            if (shopCouponBatchDTO.getThresholdAmount() != null && shopCouponBatchDTO.getParValue().compareTo(shopCouponBatchDTO.getThresholdAmount()) > 0) {
//                throw new RuntimeException("优惠卷值必须小于或等于门槛值！");
//            }
//        } else if (Objects.equals(CouponType.DISCOUNT.getCode(), type)) {
//            // 如果是折扣券，券值必须 >= 0.01 或小于 9.99折
//            if (shopCouponBatchDTO.getParValue() == null) {
//                throw new RuntimeException("优惠卷值必须大于0！");
//            }
//            if (shopCouponBatchDTO.getParValue().compareTo(BigDecimal.valueOf(0.01)) < 0 || shopCouponBatchDTO.getParValue().compareTo(BigDecimal.valueOf(9.99)) > 0) {
//                throw new RuntimeException("优惠卷值必须在0.01-9.99之间！");
//            }
//        }
//        Byte usageValidityPeriodType = shopCouponBatchDTO.getUsageValidityPeriodType();
//        if (Objects.equals(usageValidityPeriodType, CouponUsageValidityPeriodType.FIXED_DATE.getCode())) {
//            // 如果是相对时间范围内，usageStartTime 必须是当前或大于当前时间 、 如果usageEndTime不为空那必须小于usageStartTime
//            LocalDateTime usageStartTime = shopCouponBatchDTO.getUsageStartTime();
//            LocalDateTime usageEndTime = shopCouponBatchDTO.getUsageEndTime();
//            if (usageStartTime == null || usageStartTime.isBefore(LocalDateTime.now()) || (usageEndTime != null && usageEndTime.isBefore(usageStartTime))) {
//                throw new RuntimeException("使用时间范围时间无效！开始时间确保不小于当前时间，结束时间如果不为空需大于开始时间");
//            }
//        }else if (Objects.equals(usageValidityPeriodType, CouponUsageValidityPeriodType.RELATIVE_VALIDITY.getCode()) && shopCouponBatchDTO.getUsagePeriodDays() < 1) {
//            // 如果是相对时间天数，天数必须大于0
//            throw new RuntimeException("使用天数必须大于或等待1！");
//        }
//        Byte applicableType = shopCouponBatchDTO.getApplicableType();
//        if (!Objects.equals(applicableType, CouponApplicationType.NO_LIMIT.getCode())) {
//            // 如果不是适用全部，那适用逻辑不能为空且适用值不能为空
//            if (shopCouponBatchDTO.getApplicableLogic() == null || shopCouponBatchDTO.getApplicableValues() == null) {
//                throw new RuntimeException("优惠卷适用逻辑和适用值不能为空！");
//            }
//        }
//
//    }

    private void checkShopCouponBatchDTO(ShopCouponBatchDTO shopCouponBatchDTO) {
        // 1. 检查优惠券类型
        validateCouponType(shopCouponBatchDTO);
        // 2. 检查使用有效期
        validateUsageValidityPeriodTime(shopCouponBatchDTO);
        // 3. 检查适用类型
        validateApplicableType(shopCouponBatchDTO);
    }

    private void validateCouponType(ShopCouponBatchDTO shopCouponBatchDTO) {
        Byte type = shopCouponBatchDTO.getType();
        BigDecimal parValue = Optional.ofNullable(shopCouponBatchDTO.getParValue()).orElse(BigDecimal.ZERO);

        if (Objects.equals(CouponType.FULL_REDUCTION.getCode(), type)) {
            if (parValue.compareTo(BigDecimal.ZERO) <= 0 || (shopCouponBatchDTO.getThresholdAmount() != null && parValue.compareTo(shopCouponBatchDTO.getThresholdAmount()) > 0)) {
                throw new RuntimeException("优惠卷值必须大于0且小于或等于门槛值！");
            }
        } else if (Objects.equals(CouponType.DISCOUNT.getCode(), type)) {
            if (parValue.compareTo(BigDecimal.valueOf(0.01)) < 0 || parValue.compareTo(BigDecimal.valueOf(9.99)) > 0) {
                throw new RuntimeException("优惠卷值必须在0.01-9.99之间！");
            }
        }
    }

    private void validateUsageValidityPeriodTime(ShopCouponBatchDTO shopCouponBatchDTO) {
        Byte usageValidityPeriodType = shopCouponBatchDTO.getUsageValidityPeriodType();
        LocalDateTime usageStartTime = shopCouponBatchDTO.getUsageStartTime();
        LocalDateTime usageEndTime = Optional.ofNullable(shopCouponBatchDTO.getUsageEndTime()).orElse(LocalDateTime.MAX);

        if (Objects.equals(usageValidityPeriodType, CouponUsageValidityPeriodType.FIXED_DATE.getCode())) {
            if (usageStartTime == null || (usageEndTime != null && usageEndTime.isBefore(usageStartTime))) {
                throw new RuntimeException("使用时间范围时间无效！请确保存在开始时间或结束时间不为空时开始时间在结束时间之前");
            }
        } else if (Objects.equals(usageValidityPeriodType, CouponUsageValidityPeriodType.RELATIVE_VALIDITY.getCode()) && shopCouponBatchDTO.getUsagePeriodDays() < 1) {
            throw new RuntimeException("使用天数必须大于0！");
        }
    }

    private void validateApplicableType(ShopCouponBatchDTO shopCouponBatchDTO) {
        Byte applicableType = shopCouponBatchDTO.getApplicableType();
        if (!Objects.equals(applicableType, CouponApplicationType.NO_LIMIT.getCode()) &&
                (shopCouponBatchDTO.getApplicableLogic() == null || shopCouponBatchDTO.getApplicableValues() == null)) {
            throw new RuntimeException("优惠卷适用逻辑和适用值不能为空！");
        }
    }

    private void checkTimeRangeValidity(LocalDateTime startTime, LocalDateTime endTime) {
        if (startTime == null) {
            throw new RuntimeException("无开始时间，不合法的优惠卷！");
        }
        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(startTime)) {
            throw new RuntimeException("优惠卷领取未开始！");
        }
        if (endTime != null && now.isAfter(endTime)) {
            throw new RuntimeException("优惠卷已过期！");
        }

    }

    @Override
    @Transactional
    public void receiveShopPublicCoupon(Long batchId) {
        // 看是否已经领取过了，同一个不能领取
        CouponBatch couponBatch = this.baseMapper.queryCouponBatchToClaim(
                UserProvider.currentUserId(),
                batchId,
                CouponBatchStatus.CLAIMABLE.getCode(),
                IssuedType.SHOP_COUPON.getCode(),
                CouponObtainWay.SHOP_DISPLAY.getCode());
        // 这里可以做t是否该用户是否有未使用的券，如果需要只允许每个用户只有一张有效的，可以在这里限制
        if (couponBatch == null) {
            throw new RuntimeException("优惠券不可领取！");
        }
        // 发券（会检查是否超过total）
        this.sendCouponToUser(couponBatch, 1, UserProvider.currentUserId());
    }
}
